<template>
  <div class="topology-box"
    :class="[isTemplate && 'template-view-box', readonly && !formShare && 'topology-view-box', formShare && 'share-view-box']">
    <toolbar ref="toolbarRef" v-if="!readonly" v-model:canvasScale="canvasScale" :lockCanvas="isLock"></toolbar>
    <div class="meta2d-body" id="meta2d-body" :class="[!readonly && 'editor-canvas', isLock && 'lock-canvas']">
      <Graphics v-if="!readonly && !isLock"></Graphics>
      <div id="meta2d"></div>
      <PropStyle ref="propStyleRef" v-if="!readonly && !isLock"></propStyle>
    </div>
    <div class="topology-menu-box" v-if="showMenu" v-click-outside="clickOutMenu" :style="{ ...menuPos }"
      @contextmenu="menuClick">
      <div class="menu-item" :class="[item.type == 'line' && 'menu-item-line']" v-for="(item, index) in menuList"
        :key="index" v-show="!item.hidden" @click="doAction(item)">
        <div v-if="item.type != 'line'" class="menu-name">{{ item.label }}</div>
        <div v-if="item.type != 'line'" class="menu-hotkey-box">
          <div class="hotkey-item" v-for="(hItem, hIndex) in spliteHotKey(item.hotkey)" :key="hIndex">
            {{ hItem }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, watch, onUnmounted, onBeforeUnmount, getCurrentInstance, nextTick } from 'vue'
import type { ComponentInternalInstance } from 'vue'
import { useRoute } from 'vue-router'
import { useSelection } from '@/hooks/useSelection';
import { saveVersion } from '@/api/version'
import { ClickOutside as vClickOutside } from 'element-plus'
const { select, selections } = useSelection();

const $route = useRoute()
const $emit = defineEmits(['changeLatelySave', 'renderFinish'])
const instance: any = getCurrentInstance() as ComponentInternalInstance;
const proxy: any = instance.proxy as any;
import {
  Meta2d,
  Pen,
  register,
  registerAnchors,
  registerCanvasDraw,
} from '@meta2d/core';
import { flowPens, flowAnchors } from '@meta2d/flow-diagram';
import {
  activityDiagram,
  activityDiagramByCtx,
} from '@meta2d/activity-diagram';
import { classPens } from '@meta2d/class-diagram';
import { sequencePens, sequencePensbyCtx } from '@meta2d/sequence-diagram';

import { preview, getShareInfo, saveContent, uploadTemplateCover } from "@/api/common";
import { getTemplateInfo, saveTemplateContent, saveTemplate } from '@/api/template'
import toolbar from './toolbar.vue'
import Graphics from './Graphics.vue'
import PropStyle from './propStyle.vue'
import domtoimage from 'dom-to-image'
import { useHookI18n } from '@/hooks/i18n'
const { $t } = useHookI18n()
const props = defineProps({
  id: {
    type: String
  },
  readonly: {
    type: Boolean
  },
  isPrivew: {
    type: Boolean,
    default: false
  },
  formShare: {
    type: Boolean
  },
  isTemplate: {
    type: Boolean,
    default: false
  },
  docInfo: {
    type: Object,
    default: () => {
      return {};
    }
  }
})

const nodeId = ref('')
const propStyleRef = ref()
const meta2dOptions: any = {
  rule: true,
};
// const content = toRefs(
//   reactive({
//     scale: 1,
//   })
// )
const content = ref<any>({
  scale: 1
})
const menuList = ref<any[]>([{
  label: $t('message.buttons.lock'),
  value: "lock",
  hotkey: 'Ctrl+I',
  hidden: false,
}, {
  label: $t('message.buttons.copy'),
  value: "copy",
  hotkey: 'Ctrl+C',
  hidden: false,
}, {
  label: $t('message.buttons.paste'),
  value: "paste",
  hotkey: 'Ctrl+V',
  hidden: false,
}, {
  label: $t('message.buttons.cut'),
  value: "cut",
  hotkey: 'Ctrl+X',
  hidden: false,
}, {
  label: $t('message.buttons.delete'),
  value: "del",
  hotkey: 'Delete',
  hidden: false,
}, {
  type: 'line',
  hidden: false,
}, {
  label: $t('message.buttons.placeOnTop'),
  value: "top",
  hotkey: 'Ctrl+[',
  hidden: false,
}, {
  label: $t('message.buttons.placeLowerLevel'),
  value: "bottom",
  hotkey: 'Ctrl+]',
  hidden: false,
}, {
  label: $t('message.buttons.bringForward'),
  value: "up",
  hotkey: 'Shift+Ctrl+[',
  hidden: false,
}, {
  label: $t('message.buttons.sendBackward'),
  value: "down",
  hotkey: 'Shift+Ctrl+]',
  hidden: false,
}])
const showMenu = ref(false)
const menuPos = ref({ left: '0px', top: '0px' })
const canvasScale = ref(1)
const toolbarRef = ref()
const meta2dEle = ref<Meta2d>()
const isLock = ref(false)
watch(() => props.id, () => {
  destroy()
  content.value = { scale: 1 }
  getData()
})
onMounted(() => {
  if (window.previewFalse && !window.previewFalse.data) {
    window.previewFalse.data = { scale: 1 }
  }
  getData()
  if (!props.readonly) {
    document.addEventListener('keydown', handleEvent)
  }
})
onBeforeUnmount(() => {
  document.removeEventListener("keydown", handleEvent)
})
watch(() => canvasScale.value, (newV) => {
  meta2dEle.value?.scale(newV)
})
function handleEvent(e: KeyboardEvent) {
  if (isLock.value) return
  if (e.keyCode == 46 && selections.mode == 1) {
    e.preventDefault();
    showMenu.value = false
    meta2Del()
  }
  if (e.keyCode == 88 && selections.mode == 1 && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {
    e.preventDefault();
    showMenu.value = false
    meta2Cut()
  }
  if (e.keyCode == 67 && selections.mode == 1 && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {
    e.preventDefault();
    showMenu.value = false
    meta2Copy()
  }
  if (e.keyCode == 73 && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {
    e.preventDefault();
    showMenu.value = false
    meate2dLock()
  }
  if (e.keyCode == 86 && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {
    e.preventDefault();
    showMenu.value = false
    meta2Paste()
  }
  if (e.keyCode == 221 && selections.mode == 1) {
    if ((navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {
      e.preventDefault();
      showMenu.value = false
      if (e.shiftKey) {
        meta2dUp()
      } else {
        meta2dTop()
      }
    }
  }
  if (e.keyCode == 219 && selections.mode == 1) {
    if ((navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {
      e.preventDefault();
      showMenu.value = false
      if (e.shiftKey) {
        meta2dDown()
      } else {
        meta2dBottom()
      }
    }
  }
}
function getData() {
  if (props.id) {
    nodeId.value = props.id
  }
  let fun = preview
  if (props.formShare) {
    fun = getShareInfo
  }
  if ($route.query.type == 'template' || props.isTemplate) {
    fun = getTemplateInfo
  }
  fun(nodeId.value).then((res) => {
    if (res.data.code == 0 && res.data.data) {
      if (res.data.data.content && res.data.data.content.startsWith("{")) {
        let resData = JSON.parse(res.data.data.content).content;
        content.value = JSON.parse(resData);
      }
      initMeta2d()
    }
    $emit("renderFinish")
  }).catch(() => {
    $emit("renderFinish")
  })
}

function initMeta2d() {
  let newPens: any[] = []
  if (content.value) {
    if (content.value.pens) {
      content.value.pens.forEach((item: any) => {
        if (!item.ex) {
          newPens.push({ ...item, ...item.rect })
        } else {
          newPens.push(item)
        }
      });
      content.value.pens = newPens
    }
    if (content.value.locked) content.value.locked = props.readonly ? 1 : 0
    if (content.value.rule) content.value.rule = false
  }
  meta2dEle.value = new Meta2d('meta2d', meta2dOptions);
  register(flowPens());
  registerAnchors(flowAnchors());
  register(activityDiagram());
  registerCanvasDraw(activityDiagramByCtx());
  register(classPens());
  register(sequencePens());
  registerCanvasDraw(sequencePensbyCtx());
  if (content.value) {
    meta2dEle.value.open(content.value);
  }
  if (props.readonly) {
    // meta2dEle.value.fitView(true, 20);
    meta2d.scale(1);
    meta2d.centerView();
  }
  if (content.value.scale) {
    canvasScale.value = content.value.scale || 1
  } else {
    canvasScale.value = 1
  }
  // meta2dEle.value.scale(content.value.scale)
  if (!props.readonly) {
    toolbarRef.value.startOn()
    propStyleRef.value.init()
    meta2dEle.value?.on('active', active);
    meta2dEle.value?.on('inactive', inactive);
    meta2dEle.value?.on('contextmenu', showContextMenu);
    // 点击画布
    meta2dEle.value?.on('click', hideContextMenu);
    meta2dEle.value?.on('scale', meta2dScale)

    meta2dEle.value?.on('resizePens', active)
    meta2dEle.value?.on('translatingPens', moveStart)
    meta2dEle.value?.on('translatePens', moveEnd)
  }
}
const destroy = () => {
    meta2dEle.value?.off('active', active);
    meta2dEle.value?.off('inactive', inactive);
    meta2dEle.value?.off('contextmenu', showContextMenu);
    // 点击画布
    meta2dEle.value?.off('click', hideContextMenu);
    meta2dEle.value?.off('scale', meta2dScale)
    meta2dEle.value?.off('resizePens', active)
    meta2dEle.value?.off('translatingPens', moveStart)
    meta2dEle.value?.off('translatePens', moveEnd)
    meta2dEle.value?.destroy();
}
const meta2dScale = (val: number) => {
  canvasScale.value = val
}
const active = (pens?: Pen[]) => {
  select(pens)
  if (!isLock.value) {
    propStyleRef.value.changeOption()
  }
};
const moveStart = (pens?: Pen[]) => {
  select(pens);
  propStyleRef.value.changeOption('moveStart')
}
const moveEnd = (pens?: Pen[]) => {
  select(pens);
  propStyleRef.value.changeOption('moveEnd')
}
const inactive = () => {
  select();
};
function spliteHotKey(val: string) {
  return val.split("+")
}
// 展示右键菜单
function showContextMenu(e: { e: { clientY: number; clientX: string; }; clientRect: { height: number; }; }) {
  if (isLock.value) return
  menuList.value.forEach((item, index) => {
    item.hidden = false
    if (index == 0) {
      item.label = $t('message.buttons.lock')
      if (meta2d.store.data.locked == 1) {
        item.label = $t('message.buttons.unLock')
      }
    }
    if (selections.mode == 0 && [0, 2].indexOf(index) == -1) {
      item.hidden = true
    }
    if (index != 0 && meta2d.store.data.locked == 1) {
      item.hidden = true
    }
  })
  setTimeout(() => {
    showMenu.value = true
    let top = e.e.clientY - 48
    let height = selections.mode == 0 ? 80 : 321
    if (top + height > e.clientRect.height) {
      top = top - (top + height - e.clientRect.height) + 40
    }
    menuPos.value = {
      left: e.e.clientX + 'px',
      top: top + 'px'
    }
  }, 0)
}
function clickOutMenu() {
  showMenu.value = false
}
function hideContextMenu() {
  showMenu.value = false
}
function menuClick(e: { preventDefault: () => void; }) {
  e.preventDefault();
}
function doAction(item: { type: string; value: any; }) {
  if (item.type == 'line') return
  showMenu.value = false
  switch (item.value) {
    case 'copy':
      meta2Copy()
      break;
    case 'paste':
      meta2Paste()
      break;
    case 'cut':
      meta2Cut()
      break;
    case 'del':
      meta2Del()
      break;
    case 'top':
      meta2dTop();
      break;
    case 'bottom':
      meta2dBottom();
      break;
    case 'up':
      meta2dUp();
      break;
    case 'down':
      meta2dDown()
      break;
    case 'lock':
      meate2dLock()
      break;
    default:
      break;
  }
}
function meate2dLock() {
  if (meta2d.store.data.locked == 0) {
    meta2d.store.data.locked = 1
  } else if (meta2d.store.data.locked == 1) {
    meta2d.store.data.locked = 0
  }
  toolbarRef.value.changeLock()
}
function meta2Cut() {
  meta2dEle.value?.cut()
}
function meta2Copy() {
  meta2dEle.value?.copy()
}
function meta2Paste() {
  meta2dEle.value?.paste();
}
function meta2Del() {
  meta2dEle.value?.delete();
}
function meta2dTop() {
  meta2dEle.value?.top();
  meta2dEle.value?.render();
}
function meta2dBottom() {
  meta2dEle.value?.bottom();
  meta2dEle.value?.render();
}
function meta2dUp() {
  meta2dEle.value?.up();
  meta2dEle.value?.render();
}
function meta2dDown() {
  meta2dEle.value?.down();
  meta2dEle.value?.render();
}
//快捷键Ctrl+S保存截屏
function createImg(item: Record<string, any>) {
  proxy.$eventBus.$emit('changeSaveLoading', true)
  domtoimage.toPng(document.getElementById('meta2d'), { cacheBust: true }).then(function (dataUrl) {
    let formData = new FormData();
    formData.append('file', dataURLtoFile(dataUrl, item.name))
    formData.append('module', `/jvs-knowledge-ui/${props.docInfo.id}/fileCover`)
    uploadTemplateCover(formData).then(res => {
      saveTemp({
        ...props.docInfo,
        coverUrl: res.data.data.fileLink,
        coverFilePath: res.data.data.fileName,
        coverBucketName: res.data.data.bucketName,
      })
    }).catch(err => {
      proxy.$eventBus.$emit('changeSaveLoading', false)
    })
  })
}
function dataURLtoFile(dataurl: string, filename: string) {
  // 获取到base64编码
  const arr = dataurl.split(',')
  // 将base64编码转为字符串
  const bstr = window.atob(arr[1])
  let n = bstr.length
  const u8arr = new Uint8Array(n) // 创建初始化为0的，包含length个元素的无符号整型数组
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new File([u8arr], filename, {
    type: 'image/png'
  })
}
function handleSave() {
  let fun = saveContent
  if ($route.query.type == 'template') {
    createImg(props.docInfo)
    fun = saveTemplateContent
  }
  let viewString = getVal();
  fun(nodeId.value, { content: viewString }).then(res => {
    if (res.data.code == 0) {
      proxy.$onePrompt.$oneNotify({
        title: $t('message.message.saveSuccess'),
        type: "success",
        position: "bottom-right",
      });
      $emit("changeLatelySave", new Date())
    }
  });

}

function saveVersionFun() {
  let dataObj = getVal();
  saveVersion({
    dcId: props.id,
    content: JSON.stringify(dataObj)
  }).then(res => {
    if (res.data.code == 0) {
      proxy.$notify({
        title: $t('message.message.generatedVersionSuccess'),
        type: "success",
        position: "bottom-right",
      });
    }
  })
}

function getVal() {
  // return JSON.stringify(window.previewFalse.canvas.data)
  return JSON.stringify(meta2dEle.value?.data())
}
//修改模板的封面
function saveTemp(item: any) {
  saveTemplate(Object.assign({}, item, {
    typeId: item.typeId,
    templateType: 'edit',
    ...item
  })).then(res => {
    if (res.data.code == 0) {
      proxy.$notify({
        title: $t('message.message.saveTemplateSuccess'),
        type: "success",
        position: "bottom-right",
      });
    }
  }).finally(() => {
    proxy.$eventBus.$emit('changeSaveLoading', false)
  })
}
onUnmounted(() => {
  try {
    if (meta2dEle.value) meta2dEle.value?.destroy();
  } catch (error) {
    console.log(error)
  }
});
function changeEditStatus(val: boolean) {
  isLock.value = val
  clickOutMenu()
  if (val) {
    meta2d.store.data.locked = 2
  } else {
    meta2d.store.data.locked = 0
  }
  nextTick(() => {
    meta2d.resize()
    if (!val) {
      propStyleRef.value.changeOption()
    }
  })
}
function changeResize() {
  meta2d.resize()
}
function changeVersionContent(content: any, type: string) {
  if (type == 'handleVersion') {
    content.value = JSON.parse(content)
  }
}

function closeShortKey() { }

defineExpose({
  handleSave,
  closeShortKey,
  changeEditStatus,
  changeResize,
  saveVersionFun,
  changeVersionContent
})
</script>
<style lang="scss" scoped>
.topology-box {
  // height: 100%;
  width: 100%;

  background-color: #fff;

  .meta2d-body {
    height: calc(100% - 0px);
    width: 100%;

    #meta2d {
      height: 100%;
      width: 100%;
      background-color: #F5F6F7;
    }
  }

  .editor-canvas {
    height: calc(100% - 40px);
    display: grid;
    grid-template-columns: 240px 1fr 240px;
  }

  .lock-canvas {
    grid-template-columns: 1fr;
  }

  .topology-menu-box {
    position: absolute;
    z-index: 99;
    top: 0px;
    left: 0px;
    width: 200px;
    background: #FFFFFF;
    box-shadow: 0px 2px 6px 0px rgba(54, 59, 76, 0.15);
    border-radius: 4px 4px 4px 4px;
    padding: 8px;

    .menu-item {
      display: flex;
      height: 32px;
      border-radius: 4px;
      align-items: center;
      justify-content: space-between;
      padding: 0px 8px 0px 16px;
      cursor: pointer;

      .menu-name {
        font-size: 12px;
        color: #363B4C;
      }

      .menu-hotkey-box {
        display: flex;
        align-items: center;

        .hotkey-item {
          display: flex;
          align-items: center;
          height: 16px;
          background: #F5F6F7;
          border-radius: 2px 2px 2px 2px;
          padding: 0px 4px;
          font-size: 11px;
          color: #6F7588;
          box-sizing: border-box;
        }

        .hotkey-item+.hotkey-item {
          margin-left: 3px;
        }
      }

      &:hover {
        background: #F5F6F7;
      }
    }

    .menu-item-line {
      height: 1px;
      margin: 8px 0px;
      background: #EEEFF0;
    }
  }
}

.topology-view-box {
  height: calc(100vh - 200px);
}

.share-view-box {
  height: calc(100vh - 78px);
}

.template-view-box {
  height: calc(100vh - 252px);
}
</style>